/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package unc.pds.auth;

import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import unc.pds.data.Consts;
import unc.pds.exc.CannotInitSecurityException;
import unc.pds.exc.PDSSecurityException;
import unc.pds.model.GroupRemote;
import unc.pds.model.Model;
import unc.pds.model.ModelFactory;
import unc.pds.model.UserRemote;
import unc.pds.model.UserRemoteHome;
import unc.pds.util.Arch;
import unc.pds.util.ArchImpl;

/**
 *
 * @author ain
 */
public class SecurityApplyer implements SessionBean, ISecurityApplyer {

    private Hashtable permissionCache;
    private SessionContext context;
    private Long sessionKey;
    private UserRemote curUser = null;
    private Long defUserID = new Long(2);
    private String LEVEL_FRIEND = "friend";
    private String LEVEL_ANY = "any";
    private String LEVEL_NONE = "none";
    private String LEVEL_MEMBERS = "members";
    private String LEVEL_MODERATORS = "moders";
    private String MGROUP_USERS = "users";
    private String DEFAULT_USER = "default";
    private String DEFAULT_GROUP = "default";
    // <editor-fold defaultstate="collapsed" desc="EJB infrastructure methods. Click the + sign on the left to edit the code.">;

    // TODO Add code to acquire and use other enterprise resources (DataSource, JMS, enterprise bean, Web services)
    // TODO Add business methods or web service operations
    /**
     * @see javax.ejb.SessionBean#setSessionContext(javax.ejb.SessionContext)
     */
    public void setSessionContext(SessionContext aContext) {
        context = aContext;
    }

    /**
     * @see javax.ejb.SessionBean#ejbActivate()
     */
    public void ejbActivate() {
    }

    /**
     * @see javax.ejb.SessionBean#ejbPassivate()
     */
    public void ejbPassivate() {
    }

    /**
     * @see javax.ejb.SessionBean#ejbRemove()
     */
    public void ejbRemove() {
    }

    // </editor-fold>;
    /**
     * See section 7.10.3 of the EJB 2.0 specification
     * See section 7.11.3 of the EJB 2.1 specification
     */
    public void ejbCreate() {
        permissionCache = new Hashtable();
    }

    private Connection getConnection() throws NamingException, SQLException {
        DataSource ds = (DataSource) (new InitialContext()).lookup("oracle/SOS");
        return ds.getConnection();
    }

    private Long getObjectTypeID(String name) throws RemoteException {
        {
            try {
                Connection conn = getConnection();
                ResultSet ans = null;
                Statement query = null;
                try {
                    query = conn.createStatement();
                    ans = query.executeQuery("SELECT * FROM OBJECT_TYPES "
                            + "WHERE NAME='" + name + "'");
                    if (ans.next()) {
                        return new Long(ans.getLong(Consts.OBJECT_TYPES__ID));
                    } else {
                        throw new RemoteException("SecB.getObjTypeID: not such object_type " + name);
                    }
                } finally {
                    ans.close();
                    query.close();
                    conn.close();
                }
            } catch (Exception e) {
                throw new RemoteException("SecB.getObjTypeID: " + e.getLocalizedMessage());
            }
        }
    }

    private Long getAttributeID(String name, Long object_type_id) throws FinderException, RemoteException {

        try {
            Connection conn = getConnection();
            ResultSet ans = null;
            Statement query = null;
            try {
                query = conn.createStatement();
                ans = query.executeQuery("SELECT * FROM ATTRIBUTES "
                        + "WHERE OBJECT_TYPE_ID='" + object_type_id + "' AND "
                        + "NAME='" + name + "'");
                if (ans.next()) {
                    return new Long(ans.getLong(Consts.ATTRIBUTES__ATTRIBUTE_ID));
                } else {
                    throw new FinderException("SecB.getAttrID: not attr_id for " + name);
                }
            } finally {
                ans.close();
                query.close();
                conn.close();
            }
        } catch (Exception e) {
            throw new RemoteException("SecB.getAttr_id: " + e.toString());
        }
    }

    private String getPermLivel(Long objkey, String entity, String field, String act) throws RemoteException {
        if (Consts.DEBUG_MODE) System.out.println("SecAppl.getPermLevel");
        String cKey = objkey + entity + field + act;
        if (permissionCache.containsKey(cKey)) {
            return (String) permissionCache.get(cKey);
        }
        try {
            if (entity == null) {
                entity = ModelFactory.getByKey(objkey, sessionKey).getType();
            }
            Connection conn = getConnection();
            Statement query = null;
            ResultSet ans = null;
            try {
                query = conn.createStatement();
                String querry = "SELECT * FROM UPERMISSION WHERE "
                        + "OBJECT_ID=" + objkey + " AND "
                        + (entity == null ? "ENTITY IS NULL" : "ENTITY='" + entity + "'") + " AND "
                        + (act == null ? "UACTION IS NULL" : "UACTION='" + act + "'") + " AND "
                        + (field == null ? "EN_FIELD IS NULL" : "EN_FIELD='" + field + "'");
                if (Consts.DEBUG_MODE) System.out.println("SecAppl:  " + querry);
                ans = query.executeQuery(querry);
                if (ans.next()) {
                    permissionCache.put(cKey, ans.getString("REQ_LEVEL"));
                    return ans.getString("REQ_LEVEL");
                } else {
                    return null;
                }
            } finally {
                ans.close();
                query.close();
                conn.close();
            }
        } catch (Exception ex) {
            throw new RemoteException("SB.getPermission  " + ex.getLocalizedMessage());
        }
    }

    private void setPermLivel(Long objkey, String entity, String field, String act, String level) throws RemoteException {
        if (Consts.DEBUG_MODE) System.out.println("SecAppl.setPermLevel");
        try {
            Connection conn = getConnection();
            Statement query = null;
            ResultSet ans = null;
            try {
                query = conn.createStatement();
                String querry;
                if (getPermLivel(objkey, entity, field, act) != null) {
                    querry = "UPDATE UPERMISSION SET "
                            + "REQ_LEVEL='" + level + "' "
                            + " WHERE "
                            + "OBJECT_ID=" + objkey + " AND "
                            + (entity == null ? "ENTITY IS NULL" : "ENTITY='" + entity + "'") + " AND "
                            + (act == null ? "UACTION IS NULL" : "UACTION='" + act + "'") + " AND "
                            + (field == null ? "EN_FIELD IS NULL" : "EN_FIELD='" + field + "'");
                } else {
                    querry = "INSERT INTO UPERMISSION ("
                            + "OBJECT_ID,"
                            + (entity == null ? "" : "ENTITY,")
                            + (act == null ? "" : "UACTION,")
                            + (field == null ? "" : "EN_FIELD,")
                            + "REQ_LEVEL"
                            + ") VALUES ("
                            + objkey + ","
                            + (entity == null ? "" : "'" + entity + "',")
                            + (act == null ? "" : "'" + act + "',")
                            + (field == null ? "" : "'" + field + "',")
                            + "'" + level + "')";
                }
                if (Consts.DEBUG_MODE) System.out.println("SecAppl:  " + querry);
                ans = query.executeQuery(querry);
            } finally {
                ans.close();
                query.close();
                conn.close();
            }
        } catch (Exception ex) {
            throw new RemoteException("SB.getPermission  " + ex.getLocalizedMessage());
        }
    }

    class UPermission {

        Long id;
        String enity;
        String filed;
        String act;
        String level;
    }

    private UPermission[] getAllPermLevel(Long objkey) throws RemoteException {
        if (Consts.DEBUG_MODE) System.out.println("SecAppl.getAllPermLevel");
        try {
            Connection conn = getConnection();
            Statement query = null;
            ResultSet ans = null;
            try {
                query = conn.createStatement();
                String querry = "SELECT * FROM UPERMISSION WHERE "
                        + "OBJECT_ID=" + objkey + "";
                if (Consts.DEBUG_MODE) System.out.println("SecAppl:  " + querry);
                ans = query.executeQuery(querry);
                ArrayList resans = new ArrayList();
                while (ans.next()) {
                    UPermission temp = new UPermission();
                    temp.id = objkey;
                    temp.enity = ans.getString("ENTITY");
                    temp.filed = ans.getString("EN_FIELD");
                    temp.act = ans.getString("UACTION");
                    temp.level = ans.getString("REQ_LEVEL");
                    resans.add(temp);
                }
                return (UPermission[]) resans.toArray(new UPermission[resans.size()]);
            } finally {
                ans.close();
                query.close();
                conn.close();
            }
        } catch (Exception ex) {
            throw new RemoteException("SB.getPermission  " + ex.getLocalizedMessage());
        }
    }

    private void delPermissionFor(Long userkey, Long friendkey) throws RemoteException, PDSSecurityException {
        IPDSSecurity security;
        try {
            security = SecurityST.getSecurity(sessionKey);
        } catch (Exception ex) {
            throw new unc.pds.exc.CannotInitSecurityException(ex.getLocalizedMessage());
        }
        //убиваем разрешения для юзера
        UPermission[] permission = getAllPermLevel(userkey);
        security.dropAllPermission(userkey, friendkey);

        //устанавливает разрешения для его детей
        Collection childs = ModelFactory.getByParentKey(userkey, sessionKey);
        for (int i = 0; i < childs.size(); i++) {
            security.dropAllPermission(((Model) childs.toArray()[i]).getKey(), friendkey);
        }
    }

    public Long getMarkerGroupId(String name) throws RemoteException {
        try {
            Long object_type_id = getObjectTypeID(Consts.OBJECT_TYPE__MARGROUP);
            Long name_attr_id = getAttributeID(Consts.ATTR__MARGROUP_NAME, object_type_id);
            Connection conn = getConnection();
            ResultSet ans = null;
            Statement query = null;
            try {
                query = conn.createStatement();
                String sqlquerry = "SELECT * FROM PARAMS WHERE "
                        + Consts.PARAMS__ATTRIBUTE_ID + "=" + name_attr_id + " AND "
                        + Consts.PARAMS__VALUE + "='" + name + "'";
                ans = query.executeQuery(sqlquerry);
                if (ans.next()) {
                    return new Long(Long.parseLong(ans.getString(Consts.PARAMS__OBJECT_ID)));
                } else {
                    return null;
                }
            } finally {
                ans.close();
                query.close();
                conn.close();
            }
        } catch (Exception e) {
            throw new RemoteException(e.toString());
        }
    }

    public void onCreate(Long entkey) throws RemoteException, unc.pds.exc.PDSSecurityException {
        IPDSSecurity security = null;
        try {
            security = SecurityST.getSecurity(sessionKey);
        } catch (Exception ex) {
            throw new unc.pds.exc.CannotInitSecurityException(ex.toString());
        }
        String type = ModelFactory.getByKey(entkey, sessionKey).getType();
        //требуют иницыализации только поля any, так как на создании у юзера НЕТ друзяшек и прочего :3
        if (type.compareTo(Consts.OBJECT_TYPE__GROUP) == 0) {
            GroupRemote group = null;
            try {
                Arch doc = new ArchImpl("root", null);
                doc.addChild(new ArchImpl(Consts.ATTR__GROUP_NAME, DEFAULT_GROUP));
                group = (GroupRemote) ModelFactory.getByFieldSet(doc, Consts.OBJECT_TYPE__GROUP, sessionKey);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            UPermission[] permission = getAllPermLevel(group.getKey());

            for (int i = 0; i < permission.length; i++) {
                UPermission temp = permission[i];
                setPermLivel(entkey, temp.enity, temp.filed, temp.act, temp.level);
                if (temp.level.compareTo(LEVEL_ANY) == 0) {
                    security.setPermission(entkey, getMarkerGroupId(MGROUP_USERS), temp.enity, temp.filed, temp.act, true);
                }
                if (temp.level.compareTo(LEVEL_MEMBERS) == 0) {
                    security.setPermission(entkey, entkey, temp.enity, temp.filed, temp.act, true);
                }
            }
        }
        if (type.compareTo(Consts.OBJECT_TYPE__USER) == 0) {
            UserRemote user = null;
            try {
                user = ((UserRemoteHome) (new InitialContext()).lookup("ejb/User")).create(DEFAULT_USER, sessionKey);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            UPermission[] permission = getAllPermLevel(user.getKey());

            for (int i = 0; i < permission.length; i++) {
                UPermission temp = permission[i];
                setPermLivel(entkey, temp.enity, temp.filed, temp.act, temp.level);
                if (temp.level.compareTo(LEVEL_ANY) == 0) {
                    security.setPermission(entkey, getMarkerGroupId(MGROUP_USERS), temp.enity, temp.filed, temp.act, true);
                }
            }
        }
        //security.setProtectOn(entkey);
    }

    public void onNewFriend(Long userkey, Long friendkey) throws RemoteException, PDSSecurityException {
        IPDSSecurity security;
        try {
            security = SecurityST.getSecurity(sessionKey);
        } catch (Exception ex) {
            throw new unc.pds.exc.CannotInitSecurityException(ex.getLocalizedMessage());
        }
        //устанавливаем разрешения для юзера
        UPermission[] permission = getAllPermLevel(userkey);
        for (int i = 0; i < permission.length; i++) {
            if (permission[i].level.compareTo(LEVEL_FRIEND) == 0) {
                security.setPermission(userkey, friendkey, permission[i].enity, permission[i].filed, permission[i].act, true);
            }
        }

        //устанавливает разрешения для его детей
        Collection childs = ModelFactory.getByParentKey(userkey, sessionKey);
        for (int i = 0; i < childs.size(); i++) {
            permission = getAllPermLevel(((Model) childs.toArray()[i]).getKey());
            for (int j = 0; j < permission.length; j++) {
                UPermission temp = permission[j];
                if (temp.level.compareTo(LEVEL_FRIEND) == 0) {
                    security.setPermission(((Model) childs.toArray()[i]).getKey(), friendkey, temp.enity, temp.filed, temp.act, true);
                }
            }
        }
    }

    public void onDelFriend(Long userkey, Long friendkey) throws RemoteException, PDSSecurityException {
        delPermissionFor(userkey, friendkey);
    }

    public void onBanFriend(Long userkey, Long bannedUserKey) throws RemoteException, PDSSecurityException {
        //это убьет все пармишны, если они были
        onDelFriend(userkey, bannedUserKey);

        IPDSSecurity security;
        try {
            security = SecurityST.getSecurity(sessionKey);
        } catch (Exception ex) {
            throw new unc.pds.exc.CannotInitSecurityException(ex.getLocalizedMessage());
        }
        //устанавливаем запреты на уровне юзера
        UPermission[] permission = getAllPermLevel(userkey);
        for (int i = 0; i < permission.length; i++) {
            if (permission[i].level.compareTo(LEVEL_FRIEND) == 0) {
                security.setPermission(userkey, bannedUserKey, permission[i].enity, permission[i].filed, permission[i].act, false);
            }
            if (permission[i].level.compareTo(LEVEL_ANY) == 0) {
                security.setPermission(userkey, bannedUserKey, permission[i].enity, permission[i].filed, permission[i].act, false);
            }
        }
    }

    public void onUnBanFriend(Long userkey, Long bannedUserKey) throws RemoteException, PDSSecurityException {
        delPermissionFor(userkey, bannedUserKey);
    }

    public void onNewModer(Long userkey, Long friendkey) throws RemoteException, PDSSecurityException {
        IPDSSecurity security;
        try {
            security = SecurityST.getSecurity(sessionKey);
        } catch (Exception ex) {
            throw new unc.pds.exc.CannotInitSecurityException(ex.getLocalizedMessage());
        }
        //устанавливаем разрешения для юзера
        UPermission[] permission = getAllPermLevel(userkey);
        for (int i = 0; i < permission.length; i++) {
            if (permission[i].level.compareTo(LEVEL_MODERATORS) == 0) {
                security.setPermission(userkey, friendkey, permission[i].enity, permission[i].filed, permission[i].act, true);
            }
        }

        //устанавливает разрешения для его детей
        Collection childs = ModelFactory.getByParentKey(userkey, sessionKey);
        for (int i = 0; i < childs.size(); i++) {
            permission = getAllPermLevel(((Model) childs.toArray()[i]).getKey());
            for (int j = 0; j < permission.length; j++) {
                UPermission temp = permission[j];
                if (temp.level.compareTo(LEVEL_MODERATORS) == 0) {
                    security.setPermission(((Model) childs.toArray()[i]).getKey(), friendkey, temp.enity, temp.filed, temp.act, true);
                }
            }
        }
    }

    public void onDelModer(Long userkey, Long friendkey) throws RemoteException, PDSSecurityException {
        delPermissionFor(userkey, friendkey);
    }

    public void onBanMember(Long userkey, Long bannedUserKey) throws RemoteException, PDSSecurityException {
        //это убьет все пармишны, если они были
        onDelFriend(userkey, bannedUserKey);

        IPDSSecurity security;
        try {
            security = SecurityST.getSecurity(sessionKey);
        } catch (Exception ex) {
            throw new unc.pds.exc.CannotInitSecurityException(ex.getLocalizedMessage());
        }
        //устанавливаем запреты на уровне юзера
        UPermission[] permission = getAllPermLevel(userkey);
        for (int i = 0; i < permission.length; i++) {
            if (permission[i].level.compareTo(LEVEL_MEMBERS) == 0) {
                security.setPermission(userkey, bannedUserKey, permission[i].enity, permission[i].filed, permission[i].act, false);
            }
            if (permission[i].level.compareTo(LEVEL_ANY) == 0) {
                security.setPermission(userkey, bannedUserKey, permission[i].enity, permission[i].filed, permission[i].act, false);
            }
        }
    }

    public void onUnBanMember(Long userkey, Long bannedUserKey) throws RemoteException, PDSSecurityException {
        delPermissionFor(userkey, bannedUserKey);
    }

    public void onPermissionChange(Long entKey, String act, String level) throws RemoteException, PDSSecurityException {
        onPermissionChange(entKey, null, null, act, level);
    }

    public void onPermissionChange(Long user, String entity, String act, String level) throws RemoteException, PDSSecurityException {
        onPermissionChange(user, entity, null, act, level);
    }

    public void onPermissionChange(Long entKey, String entity, String field, String act, String level) throws RemoteException, PDSSecurityException {
        IPDSSecurity security;
        try {
            security = SecurityST.getSecurity(sessionKey);
        } catch (Exception ex) {
            throw new unc.pds.exc.CannotInitSecurityException(ex.getLocalizedMessage());
        }
        setPermLivel(entKey, entity, field, act, level);
        if (level.compareTo(LEVEL_FRIEND) == 0) {
            security.dropPermission(entKey, entity, field, act);
            Model m1 = ModelFactory.getByKey(entKey, sessionKey);
            while (m1.getParent() != null) {
                m1 = m1.getParent();
            }
            UserRemote user = (UserRemote) m1;
            UserRemote[] friends = user.getFriends();
            for (int i = 0; i < friends.length; i++) {
                security.setPermission(entKey, friends[i].getKey(), entity, field, act, true);
            }
        }
        if (level.compareTo(LEVEL_ANY) == 0) {
            security.dropPermission(entKey, entity, field, act);
            security.setPermission(entKey, getMarkerGroupId(MGROUP_USERS), entity, field, act, true);
        }
        if (level.compareTo(LEVEL_NONE) == 0) {
            security.dropPermission(entKey, entity, field, act);
        }
    }
}
